//
// DotVersionOperator
//
// Copyright (C) jextra.net.
//
//  This file is part of the net.jextra.dep Library.
//
//  The net.jextra.dep Library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  The net.jextra.dep Library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with the net.jextra.dep Library; if not, write to the Free
//  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
//  02111-1307 USA.
//

package net.jextra.dep.repo.version;

import java.util.*;
import java.util.regex.*;

/**
 * <p>
 * Version number that is represented by a sequence of period (dot) separated numbers and letters.
 * </p>
 */
public class DotVersionOperator implements VersionOperator
{
    // ============================================================
    // Methods
    // ============================================================

    // ----------
    // public
    // ----------

    @Override
    public String getStartingVersionString()
    {
        return "0.0.0-0";
    }

    /**
     * Auto-increment the given version number to the next "newest" version number. For example:
     * <br>
     * VersionNumber=1.2.0-13
     * <br>
     * Would be incremented to equal:
     * <br>
     * VersionNumber=1.2.0-14
     * <br>
     */
    @Override
    public String getNextVersionNumber( String versionNumber )
    {
        if ( versionNumber == null )
        {
            return getStartingVersionString();
        }

        // Pull version number apart.
        VersionBreakdown vb = new VersionBreakdown( versionNumber );

        // Put back together.
        StringBuffer buffer = new StringBuffer();
        for ( int i = 0; i < vb.getSepCount(); i++ )
        {
            buffer.append( vb.getNum( i ) );
            buffer.append( vb.getSep( i ) );
        }
        buffer.append( autoIncrement( vb.getTail() ) );

        return buffer.toString();
    }

    @Override
    public int compare( String version1, String version2 )
    {
        VersionBreakdown vb1 = new VersionBreakdown( version1 );
        VersionBreakdown vb2 = new VersionBreakdown( version2 );

        int minLength = vb1.getSepCount();
        if ( vb2.getSepCount() < minLength )
        {
            minLength = vb2.getSepCount();
        }

        for ( int i = 0; i < minLength; i++ )
        {
            int c = compareInts( vb1.getNum( i ), vb2.getNum( i ) );
            if ( c != 0 )
            {
                return c;
            }
        }

        int c = compareInts( vb1.getTail(), vb2.getTail() );
        if ( c != 0 )
        {
            return c;
        }

        return vb1.getSepCount() - vb2.getSepCount();
    }

    // ----------
    // private
    // ----------

    private int compareInts( String intString1, String intString2 )
    {
        String letters1 = getLetters( intString1 );
        String letters2 = getLetters( intString2 );
        int digits1 = getDigits( intString1 );
        int digits2 = getDigits( intString2 );

        // If text portions are same, compare digit portions.
        if ( letters1.equals( letters2 ) )
        {
            return digits1 - digits2;
        }

        // If first only had digits, and second did not, only digits wins.
        if ( letters1.length() == 0 && letters2.length() > 0 )
        {
            return 1;
        }

        // If second only had digits, and first did not, only digits wins.
        if ( letters2.length() == 0 && letters1.length() > 0 )
        {
            return -1;
        }

        return letters1.compareTo( letters2 );
    }

    private String autoIncrement( String value )
    {
        int digits = getDigits( value );
        digits++;
        String letters = getLetters( value );

        return letters + digits;
    }

    private String getLetters( String string )
    {
        StringBuffer buffer = new StringBuffer();
        for ( int i = 0; i < string.length(); i++ )
        {
            char c = string.charAt( i );
            if ( !Character.isDigit( c ) )
            {
                buffer.append( c );
            }
        }

        return buffer.toString();
    }

    private int getDigits( String string )
    {
        StringBuffer buffer = new StringBuffer();
        for ( int i = 0; i < string.length(); i++ )
        {
            char c = string.charAt( i );
            if ( Character.isDigit( c ) )
            {
                buffer.append( c );
            }
        }

        if ( buffer.length() == 0 )
        {
            buffer.append( "-1" );
        }

        return Integer.parseInt( buffer.toString() );
    }

    // ============================================================
    // Inner Classes
    // ============================================================

    private class VersionBreakdown
    {
        private ArrayList<String> nums = new ArrayList<String>();
        private ArrayList<String> seps = new ArrayList<String>();
        private String tail;

        public VersionBreakdown( String versionNumber )
        {
            Pattern pattern = Pattern.compile( "([^.\\-:/]+)([.\\-:/])(.*)" );
            String string = versionNumber;
            for ( boolean matches = true; matches; )
            {
                Matcher m = pattern.matcher( string );
                matches = m.matches();
                if ( matches )
                {
                    nums.add( m.group( 1 ) );
                    seps.add( m.group( 2 ) );
                    string = m.group( 3 );
                }
            }
            tail = string;
        }

        public int getSepCount()
        {
            return nums.size();
        }

        public String getNum( int i )
        {
            return nums.get( i );
        }

        public String getSep( int i )
        {
            return seps.get( i );
        }

        public String getTail()
        {
            return tail;
        }
    }
}
